package com.example.chat;

import com.example.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.*;
import java.net.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
@Component
public class UDPServer {
    private static int udpPort;

    private static int tcpPort;

    @Value("${print.file.chatFile}")
    String chatFilePath;

    private DatagramSocket socket;

    public static List<Socket> onLinesSockets = new ArrayList<>();                       //所有在线的对象连接

    public static ServerSocket serverSocket;                                    //TCP端口号


    @Autowired
    public void setMyProperty(Environment env){                                  //从配置文件中注入静态成员变量

        /**
         * 静态成员变量，注入方式用这种 Environment 获取到当前环境，然后操作
         * @Value 的方式不能注入
         */
        tcpPort = Integer.valueOf(env.getProperty("chat.tcp.port"));
        udpPort = Integer.valueOf(env.getProperty("chat.udp.port"));
        try {
            serverSocket = new ServerSocket(tcpPort);
        } catch (IOException e) {
            log.error("服务端注册TCP端口失败！");
        }
    }

    @PostConstruct
    public void startServer() {
        try {
            socket = new DatagramSocket(udpPort);
            byte[] buffer = new byte[1024 * 64];

            /**
             * 创建一个线程循环监听
             * 端口：udp.port对应的value
             * 监听用户发来的消息  监听该com.example.chat.UDPClient.sendMessage方法
             */
            new Thread(() -> {
                while (true) {

                    DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
                    try {
                        socket.receive(packet);
                    } catch (IOException e) {
                        log.info("服务器结束聊天！");
                        break;
                    }

                    // 处理接收到的消息
                    String data = new String(packet.getData(), 0, packet.getLength());
                    Map<String, String> map = parseStringToMap(data);                                   //将字符串转换为hashmap
                    String message = map.get("message");
                    log.info(map.toString());

                    log.info("聊天室服务端  --->>>  接受消息：{}",message);
                    printChatFile(map);

                    // 广播消息给所有客户端
                    try {
                        broadcastMessage(message);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                }
            }).start();


            //开启一个线程用来作为服务端接受连接，顺便添加在线的连接，以便与发消息给所有的客户端
            new Thread(() -> {
                while (true){
                    Socket accept = null;
                    try {
                        accept = serverSocket.accept();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    onLinesSockets.add(accept);
                }
            }).start();

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     *TCP协议发送给所有客户端消息
     */
    public void broadcastMessage(String message) throws IOException {

            for (Socket onLineSocket : onLinesSockets) {

                DataOutputStream dos = new DataOutputStream(onLineSocket.getOutputStream());
                dos.writeUTF(message);
                dos.flush();
            }
    }

    @PreDestroy
    public void stopServer() {
        if (socket != null && !socket.isClosed()) {
            socket.close();
        }
    }


    //该方法用于将 K V 格式的字符串转换为map集合
    public Map<String, String> parseStringToMap(String input) {             //将字符串解析为hashmap

        /**
         * 该方法用于将 K V 格式的字符串转换为map集合
         */

        Map<String, String> resultMap = new HashMap<>();

        // 匹配大括号中的键值对
        Pattern pattern = Pattern.compile("\\{(.*?)}");
        Matcher matcher = pattern.matcher(input);

        if (matcher.find()) {
            String keyValuePairs = matcher.group(1);

            // 分割键值对并存入HashMap
            String[] pairs = keyValuePairs.split(",\\s*");
            for (String pair : pairs) {
                String[] entry = pair.split("=", 2);
                if (entry.length == 2) {
                    String key = entry[0].trim();
                    String value = entry[1].trim();
                    resultMap.put(key, value);
                }
            }
        }

        return resultMap;
    }

    /**
     * 检查文件是否存在，不存在则创建
     * @param filePath
     */
    public void checkChatFilePathExits(String filePath) {
        File file = new File(filePath);
        String parentFolderPath = file.getParent();
        File parentFolder = new File(parentFolderPath);
        boolean created = parentFolder.mkdirs();                        // 使用mkdirs()以确保创建多层文件夹

        if (created) {
            log.info("创建完毕！");
        }
    }

    /**
     * 将接受的消息打印到文件中
     * @param map
     */
    private void printChatFile(Map<String, String> map) {

        /**
         * 该方法用于将发来的消息打印到文件中
         */

        checkChatFilePathExits(chatFilePath);                                   //检查文件是否存在

        String jwt = map.get("jwt");
        String message = map.get("message");

        Claims claims = null;
        try {
            claims = JwtUtils.parseJWT(jwt);
        } catch (Exception e) {
            log.info("断开连接");
        }

        String name = (String) claims.get("name");
        String studentID = (String) claims.get("studentID");

        try (
                PrintStream ps = new PrintStream(new FileOutputStream(chatFilePath,true),true)
                ){

            LocalDateTime now = LocalDateTime.now();
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss SSS");           // 格式化日期时间和毫秒
            String formattedDateTime = now.format(formatter);

            String s = new StringBuilder()
                    .append(formattedDateTime).append(" ")
                    .append(studentID).append(" ")
                    .append(name).append("：")
                    .append(message).toString();
            ps.println(s);
        }catch (Exception e){
            e.printStackTrace();
        }

    }
}
